package cn.iocoder.yudao.module.pay.splitting.service.core;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.business.basic.service.impl.AbstractService;
import cn.iocoder.yudao.framework.common.exception.Assertions;
import cn.iocoder.yudao.module.api.infrastructure.config.ConfigApiV2;
import cn.iocoder.yudao.module.api.infrastructure.config.dto.PayConfigRespDTO;
import cn.iocoder.yudao.module.api.pay.order.PayOrderApi;
import cn.iocoder.yudao.module.api.pay.order.eums.UnifiedPayChannelEnum;
import cn.iocoder.yudao.module.api.pay.order.vo.PayOrderVO;
import cn.iocoder.yudao.module.api.pay.transfer.dto.PayTransferCreateDTO;
import cn.iocoder.yudao.module.api.pay.transfer.enums.UnifiedTransferChannelEnum;
import cn.iocoder.yudao.module.pay.framework.impl.mallbook.MallBookPayClientConfig;
import cn.iocoder.yudao.module.pay.framework.impl.mallbook.domain.CompleteNotifyResponse;
import cn.iocoder.yudao.module.pay.splitting.convert.PaySplittingConvert;
import cn.iocoder.yudao.module.pay.splitting.dao.PaySplittingMapper;
import cn.iocoder.yudao.module.pay.splitting.enums.SplittingStatusEnum;
import cn.iocoder.yudao.module.pay.splitting.enums.SplittingTypeEnum;
import cn.iocoder.yudao.module.pay.splitting.mq.SplittingProducer;
import cn.iocoder.yudao.module.pay.splitting.pojo.bo.PaySplittingBO;
import cn.iocoder.yudao.module.pay.splitting.pojo.po.PaySplitting;
import cn.iocoder.yudao.module.pay.splitting.pojo.vo.MallBookBalanceVO;
import cn.iocoder.yudao.module.pay.splitting.pojo.vo.MallBookSplittingVO;
import cn.iocoder.yudao.module.pay.splitting.pojo.vo.PaySplittingVO;
import cn.iocoder.yudao.module.pay.transfer.service.PayTransferService;
import cn.iocoder.yudao.module.pay.wechat.dao.WechatOrderInfoMapper;
import com.trhui.mallbook.client.BalanceQueryClient;
import com.trhui.mallbook.client.CustomCompleteClient;
import com.trhui.mallbook.client.ReceiveClient;
import com.trhui.mallbook.domain.common.BaseResponse;
import com.trhui.mallbook.domain.request.PaymentOrderUser;
import com.trhui.mallbook.domain.request.QueryBalanceRequest;
import com.trhui.mallbook.domain.request.hf.HfCompleteRequest;
import com.trhui.mallbook.domain.request.hf.HfReceiveRequest;
import com.trhui.mallbook.domain.request.hf.ReceiveUser;
import com.trhui.mallbook.domain.response.QueryBalanceResponse;
import com.trhui.mallbook.domain.response.hf.HfReceiveResponse;
import com.trhui.mallbook.enums.ChannelTypeEnum;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static cn.iocoder.yudao.module.api.infrastructure.ConfigConstants.CONFIG_PAY;

/**
 * <pre>
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 * 服务
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 * </pre>
 *
 * @author 山野羡民（1032694760@qq.com）
 * @since 2025/07/22
 */
@Slf4j
@Service
public class CorePaySplittingService extends AbstractService<PaySplitting, PaySplittingVO> {
    @Resource
    private PaySplittingMapper paySplittingMapper;
    @Resource
    private WechatOrderInfoMapper wechatOrderInfoMapper;

    @Resource
    private SplittingProducer splittingProducer;

    @Resource
    @Lazy // 延迟加载，避免循环依赖
    private PayTransferService payTransferService;

    @Resource
    private PayOrderApi payOrderApi;
    @Resource
    private ConfigApiV2 configApiV2;

    @Override
    public PaySplittingMapper getInjectedMapper() {
        return paySplittingMapper;
    }

    @Override
    public PaySplittingVO convertToViewObject(PaySplitting entity) {
        return PaySplittingConvert.INSTANCE.toViewObject(entity);
    }

    public int requestSplitting() {
        // 因为历史数据会很多，此处只获取获取三个月前至今的订单
        LocalDateTime minDateTime = LocalDateTime.now().minusMonths(3);
        // 目前只有微信支付订单需要分账
        List<PaySplittingBO> payNoFromWechatOrder = wechatOrderInfoMapper.selectByMinDateTimeAndCanSplitting(minDateTime);
        if (CollUtil.isEmpty(payNoFromWechatOrder)) {
            return 0;
        }
        int count = 0;
        for (PaySplittingBO splitting : payNoFromWechatOrder) {
            splittingProducer.sendSplittingRequestMessage(splitting.getPayNo(), splitting.getSplitAmount());
            count++;
            // 延时一下，避免并发请求
            ThreadUtil.safeSleep(200);
        }
        return count;
    }

    public void doDepositAsyncSplitting(String payNo, Long splitAmount) {
        PayOrderVO payOrderVO = payOrderApi.getOrderByNo(payNo);
        if (payOrderVO == null) {
            log.warn("[doDepositAsyncSplitting] 支付单({})不存在，不处理 MallBook 分账", payNo);
            return;
        }
        if (!UnifiedPayChannelEnum.isWxLite(payOrderVO.getChannelCode())) {
            // 注：目前人民币支付方式只支持微信小程序支付，故而只处理微信小程序支付渠道
            log.warn("[doDepositAsyncSplitting] 支付单({})非微信小程序支付，不处理 MallBook 分账]", payNo);
            return;
        }
        String splitUserId = getSplitUserId(payOrderVO);
        String notifyUrl = configApiV2.getConfig(CONFIG_PAY, PayConfigRespDTO.class).getSplittingNotifyUrl();
        MallBookSplittingVO result = doSplitting(payNo, splitAmount, splitUserId, notifyUrl);
        paySplittingMapper.saveSyncResult(payNo, splitAmount, splitUserId, result);
        wechatOrderInfoMapper.updateSplitStatus(payNo, result.getStatus(), result.getChannelErrorMsg());
        if (Objects.equals(result.getStatus(), SplittingStatusEnum.SUCCESS.getStatus())) {
            ThreadUtil.execAsync(() -> {
                // 有人反映说调用异步分账10秒后才能调用确认收货接口 https://o2yyifwsjv.feishu.cn/docx/WaV7diAKMoNwt2xsfRVcXwxZn3d?comment_id=7504117477917573124&comment_type=0&comment_anchor=true
                ThreadUtil.safeSleep(10 * 1000);
                boolean success = doReceive(payNo, result.getSplitNo(), splitAmount, splitUserId);
                paySplittingMapper.updateReceived(payNo, success);
            });
        }
    }

    private String getSplitUserId(PayOrderVO payOrderVO) {
        /*try {
            String payNo = payOrderVO.getNo();
            Long shopId;
            if (StrUtil.startWith(payOrderVO.getTradeNo(), SCAN_ORDER_NO_PREFIX)) {
                shopId = scanOrderApi.getByTradeNo(payOrderVO.getTradeNo()).getShopId();
            } else if (StrUtil.startWith(payOrderVO.getTradeNo(), TRADE_ORDER_NO_PREFIX)) {
                shopId = tradeOrderApi.getByTradeNo(payOrderVO.getTradeNo()).getShopId();
            } else {
                throw new KnownServiceException("支付单(" + payNo + ")对应的业务交易单(" + payOrderVO.getTradeNo() + ")不支持子商户分账");
            }
            if (shopId == null || shopId <= 0L) {
                throw new KnownServiceException("支付单(" + payNo + ")未获取到店铺编号，无法获取对应的子商户进行分账");
            }
            ShopInfoDTO shopInfo = shopInfoApi.getShopInfo(shopId);
            Assertions.notNull(shopInfo, "支付单(" + payNo + ")获取店铺(" + shopId + ")信息为空，无法获取对应的子商户进行分账");
            SplittingMerchant splittingMerchant = coreSplittingMerchantService.getOrCreate(shopInfo.getContactMobile());
            String splitUserId = splittingMerchant.getChannelUserId();
            Assertions.notBlank(splitUserId, "支付单(" + payNo + ")对应的店铺(" + shopId + ")用于分账的子商户号未注册成功：#" + shopInfo.getContactMobile());
            return splitUserId;
        } catch (Exception e) {
            log.error("获取子商户分账用户编号失败，使用平台商户号对应的分账用户编号代替]", e);
            return MallBookPayClientConfig.Holder.INSTANCE.getMerchantUserId();
        }*/
        // 注：暂时先分账到平台的商户号，再由平台通过“充值+转账”的方式实现提现，后续完善规则后再考虑分账到商家的子商户
        return MallBookPayClientConfig.Holder.INSTANCE.getMerchantUserId();
    }

    private static MallBookSplittingVO doSplitting(String payNo, Long splitAmount, String splitUserId, String notifyUrl) {
        //异步分账业务说明
        //- 注：当支付订单为异步分账时，后续可以使用该接口指定分账方。
        //- 异步订单（只支持一次异步分账）案例
        //  - 支付订单100元，在进行异步分账时必须将这100元全部分完。例：
        //  - 分账方A异步分账40元，分账方B异步分账60元。
        //- 异步订单（支持多次异步分账）案例
        //  - 支付订单100元。
        //  - 第1次给分账方A异步分账10元，分账方B异步分账20元。
        //  - 第2次给分账方A异步分账20元，分账方C异步分账50元。
        //  - 总分账金额不能超过支付订单100元。
        // 接口文档，参阅：https://o2yyifwsjv.feishu.cn/docx/WaV7diAKMoNwt2xsfRVcXwxZn3d#G2Zod076FoIqm0xAeL7cvr6nnTh
        HfCompleteRequest request = new HfCompleteRequest();
        request.setMerchantNo(MallBookPayClientConfig.Holder.INSTANCE.getMerchantNo());
        String splitNo = MallBookPayClientConfig.Holder.INSTANCE.generateRequestNo();
        request.setMerOrderId(splitNo);
        /*
         * 原支付请求订单号
         */
        request.setOriginalMerOrderId(payNo);
        /*
         * 分账方列表
         */
        request.setSplitList(buildSplitListForPayee(splitUserId, splitAmount));
        /*
         * 后台回调地址(必填) 字段长度最长：256位
         */
        request.setNotifyUrl(notifyUrl);
        /*
         * 扩展参数1
         */
        request.setParameter1(payNo);
        //BaseResponse<HfCompleteResponse> response = CompleteClient.hfComplete(request);
        BaseResponse<CompleteNotifyResponse> response = CustomCompleteClient.hfCompleteV2(request);
        log.info("CompleteClient.hfComplete: code={}, msg={}", response.getCode(), response.getMsg());
        MallBookSplittingVO result;
        if (!response.success()) {
            result = MallBookSplittingVO.errorOf(payNo, splitNo, response.getCode(), response.getMsg(), response);
        } else if ("1".equals(response.getData().getStatus())) {
            result = MallBookSplittingVO.successOf(payNo, splitNo, response);
        } else if ("2".equals(response.getData().getStatus())) {
            result = MallBookSplittingVO.failureOf(payNo, splitNo, response.getCode(), response.getData().getStatusMsg(), response);
        } else {
            result = MallBookSplittingVO.waitingOf(payNo, splitNo, response);
        }
        return result;
    }

    private static List<PaymentOrderUser> buildSplitListForPayee(String splitUserId, Long splitAmount) {
        PaymentOrderUser orderUser = new PaymentOrderUser();
        /*
         * 分账方会员ID（必填） 字段长度最长：20位
         */
        orderUser.setSplitUserId(splitUserId);
        /*
         * 分账金额（必填） 字段长度最长：12位
         */
        orderUser.setSplitAmount(String.valueOf(splitAmount));
        /*
         * 分账类型（必填）字段长度最长：1位
         * 0：收单金额
         * 1：分账金额
         * 2：佣金
         */
        orderUser.setSplitType(String.valueOf(SplittingTypeEnum.Payee.getValue()));
        return List.of(orderUser);
    }

    private static void cancelSplitting(String payNo, String splitNo, String notifyUrl) {
        throw new UnsupportedOperationException("待实现");
        //- 分账归还业务说明：
        //1. 当平台商户分账错误，可通过分账归还接口，将资金从分账方退回到平台方的【运营资金】账户或收款人的【在途资金】账户。
        //2. 仅支持异步分账订单的分账归还；同步分账的支付订单不支持归还。
        //3. 余额支付的异步分账订单不支持归还。
        //4. 异步分账订单，只有做完确认收货之后才可归还（扣分账方的可结算）。
        //5. 异步分账订单，如果有发生过退款，不可归还。
        //6. 分账归还是将整笔异步分账全部归还，不可部分归还。
        //7. 分账归还后，可再重新做异步分账。
    }

    public int receiveSplitting() {
        // 因为历史数据会很多，此处只获取获取三个月前至今的订单
        LocalDateTime minDateTime = LocalDateTime.now().minusMonths(3);
        List<PaySplitting> paySplittings = paySplittingMapper.selectByMinDateTimeAndCanReceived(minDateTime);
        int count = 0;
        for (PaySplitting paySplitting : paySplittings) {
            boolean success = doReceive(paySplitting.getPayNo(), paySplitting.getSplitNo(), paySplitting.getAmount(), paySplitting.getSplitUserId());
            paySplittingMapper.updateReceived(paySplitting.getPayNo(), success);
            count++;
            // 延时一下，避免并发请求
            ThreadUtil.safeSleep(200);
        }
        return count;
    }

    private static boolean doReceive(String payNo, String splitNo, Long splitAmount, String splitUserId) {
        //注：支持一次全额或多次部分确认收货，如果支付订单为异步订单，在确认收货前必须先进行异步分账。
        //- 多次部分确认收货，对于某个分账方的当前确认收货+已确认收货+未确认收货退款金额不能大于该分账方的原分账金额。
        //- 非异步订单案例
        //  - 原支付订单的分账方A ，分账金额为100元。
        //  - 分账方A当前确认收货金额+已确认收货总额+未确认收货退款总额不能大于100元。
        //- 异步订单（只支持一次异步分账）案例
        //  - 原支付订单交易金额为100元。
        //  - 分账方A异步分账40元，分账方B异步分账60元。
        //  - 分账方A当前确认收货金额+已确认收货总额+未确认收货退款总额不能大于40元。
        //  - 分账方B当前确认收货金额+已确认收货总额+未确认收货退款总额不能大于60元。
        //- 异步订单（支持多次异步分账）案例
        //  - 原支付订单交易金额为100元。
        //  - 第1次给分账方A异步分账40元，异步分账订单号A0001
        //  - 异步分账订单号A0001，分账方A当前确认收货金额+已确认收货总额+未确认收货退款总额不能大于40元。
        //  - 第2次给分账方A异步分账60元，异步分账订单号A0002
        //  - 异步分账订单号A0002，分账方A当前确认收货金额+已确认收货总额+未确认收货退款总额不能大于60元。
        log.debug("[doReceiveSleepAsync][payNo({}) amount({})]", payNo, splitAmount);
        HfReceiveRequest request = new HfReceiveRequest();
        request.setMerchantNo(MallBookPayClientConfig.Holder.INSTANCE.getMerchantNo());
        request.setMerOrderId(MallBookPayClientConfig.Holder.INSTANCE.generateRequestNo());
        /*
         * 原支付请求订单号(必填) 字段长度最长：32位
         */
        request.setOriginalMerOrderId(payNo);
        /*
         * 异步分账订单号 字段长度最长：32位
         */
        request.setAsynMerOrderId(splitNo);
        /*
         * 确认收货总额(必填) 必须大于0 且要等于总确认分账金额 字段长度最长：12位
         */
        request.setRcvAmount(String.valueOf(splitAmount));
        /*
         * 自定义参数1 字段长度最长：100位
         */
        //request.setParameter1("参数1");
        /*
         * 自定义参数2 字段长度最长：100位
         */
        //request.setParameter2("参数2");
        /*
         * 分账方列表(必填)
         */
        List<ReceiveUser> splitList = new ArrayList<>();
        ReceiveUser receiveUser = new ReceiveUser();
        /*
         * 分账方子商户编号(必填) 字段长度最长：20位
         */
        receiveUser.setSplitUserId(splitUserId);
        /*
         * 确认分账金额(必填) 字段长度最长：12位
         */
        receiveUser.setRcvSplitAmount(String.valueOf(splitAmount));
        splitList.add(receiveUser);
        request.setSplitList(splitList);
        /*
         * 商品列表
         */
        //List<ReceiveGood> goodList = new ArrayList<>();
        //ReceiveGood good = new ReceiveGood();
        /*
         * 商品编号 字段长度最长：64位
         */
        //good.setGoodsId("123");
        /*
         * 商品名称(必填) 字段长度最长：64位
         */
        //good.setGoodsName("商品1");
        /*
         * 商品数量 字段长度最长：32位
         */
        //good.setQuantity("1");
        /*
         * 商品价格 字段长度最长：32位
         */
        //good.setPrice("1");
        //goodList.add(good);
        //request.setGoodsDetail(goodList);
        BaseResponse<HfReceiveResponse> response = ReceiveClient.hfReceive(request);
        log.info("[doReceive] ReceiveClient.hfReceive: code={}, msg={}", response.getCode(), response.getMsg());
        return response.success();
    }

    public MallBookBalanceVO queryPlatformBalance() {
        QueryBalanceResponse response = queryBalance(MallBookPayClientConfig.Holder.INSTANCE.getMerchantUserId());
        return new MallBookBalanceVO()
                .setTransitAmount(NumberUtil.parseInt(response.getTransitAmount()))
                .setUnsettleAmount(NumberUtil.parseInt(response.getUnsettleAmount()))
                .setSettledAmount(NumberUtil.parseInt(response.getSettledAmount()))
                .setMarketingAmount(NumberUtil.parseInt(response.getMarketingAmount()));
    }

    private static QueryBalanceResponse queryBalance(String channelUserId) {
        QueryBalanceRequest request = new QueryBalanceRequest();
        request.setMerchantNo(MallBookPayClientConfig.Holder.INSTANCE.getMerchantNo());
        request.setMerOrderId(MallBookPayClientConfig.Holder.INSTANCE.generateRequestNo());
        request.setUserId(channelUserId);
        BaseResponse<QueryBalanceResponse> response = BalanceQueryClient.queryBalance(request, ChannelTypeEnum.HF);
        log.info("[queryBalance] BalanceQueryClient.queryBalance: code={}, msg={}", response.getCode(), response.getMsg());
        Assertions.isTrue(response.success(), response.getMsg());
        return response.getData();
    }

    public boolean withdrawPlatformBalance(Integer amount) {
        Assertions.isTrue(amount > 0, "提现金额无效");
        String channelUserId = MallBookPayClientConfig.Holder.INSTANCE.getMerchantUserId();
        QueryBalanceResponse response = queryBalance(channelUserId);
        Assertions.isTrue(NumberUtil.parseInt(response.getSettledAmount()) >= amount, "可提现金额不足");
        PayTransferCreateDTO dto = new PayTransferCreateDTO();
        dto.setTransferNo(MallBookPayClientConfig.Holder.INSTANCE.generateRequestNo());
        dto.setChannelCode(UnifiedTransferChannelEnum.MALLBOOK.getCode());
        dto.setPrice(amount);// 以实际到账金额为准，提现手续费由第三方渠道代扣
        dto.setSubject("管理员提现");
        dto.setUserAccount(channelUserId);
        dto.setUserName("益连弘");
        payTransferService.createTransfer(dto);
        return true;
    }

}
